package services;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import models.ClientApiAccess;
import models.Event;
import models.GCalendarToken;
import models.YouUser;

import org.codehaus.jackson.JsonNode;

import play.libs.F.Function;
import play.libs.F.Promise;
import play.libs.WS;
import play.libs.WS.Response;
import util.DateUtil;

public class GCalendarWrapper {
	private static final String EVENTS_PRIMARY_CALENDAR_URL = "https://www.googleapis.com/calendar/v3/calendars/primary/events";
	
	/**
	 * Get the received token, add ii to the informed  user and store/update.
	 * 
	 * @param youId
	 * @param tokenResponse
	 * @return Promise<YouUser>
	 */
	public static Promise<YouUser> exchangeTokenAndStore(final Long youId, String authcode, ClientApiAccess calendarApiAccess) {
			Promise<Response> tokenResponse = exchangeCodeForToken(authcode, calendarApiAccess);	
			Promise<YouUser> result = tokenResponse
				.map(new Function<Response, YouUser>() {
					@Override
					public YouUser apply(Response a) throws Throwable {
						
						// create token object (json -> token object)
						JsonNode node = a.asJson();
						String accessToken = node.get("access_token").getTextValue();
						String tokenType = node.get("token_type").getTextValue();
						long expiresIn = node.get("expires_in").asLong();
						long now = System.currentTimeMillis();
						GCalendarToken data = GCalendarToken.create(accessToken, tokenType, expiresIn, now);
						
						// add token to the existing user and store
						YouUser user = YouUser.find.byId(youId);
						user.addAccount(data);
						return user;
					}
				});

		return result;
	}
	
	/**
	 * 1. Token Request (response_type=code)
	 * 2. User login and consent
	 * 3. Receive code
	 * 4. Exchange Code For Token <======
	 * 5. Token response
	 * 
	 * Call the service that exchange the access code for token.
	 * 
	 * @param code
	 * @return
	 */
	public static Promise<Response> exchangeCodeForToken(String code, ClientApiAccess clientApiAccess) {
			
			// parameters to post
			StringBuffer sb = new StringBuffer();
			sb.append("grant_type=authorization_code");
			sb.append("&client_id=");
			sb.append(clientApiAccess.getClientId());
			sb.append("&redirect_uri=");
			sb.append(clientApiAccess.getRedirect());
			sb.append("&client_secret=");
			sb.append(clientApiAccess.getSecret());
			sb.append("&code=");
			try {
				sb.append(URLEncoder.encode(code, "UTF-8"));
			} catch (UnsupportedEncodingException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			return WS.url(clientApiAccess.getTokenURL())
					.setContentType("application/x-www-form-urlencoded")
					.post(sb.toString());
	}
	
	public static Promise<List<Event>> search(YouUser user, ClientApiAccess clientApiAccess, String query) {
		Promise<Response> searchResponse = searchResponse(user, clientApiAccess, query);	
		Promise<List<Event>> result = searchResponse
			.map(new Function<Response, List<Event>>() {
				@Override
				public List<Event> apply(Response a) throws Throwable {
					List<Event> events = new ArrayList<Event>();	;
					// create token object (json -> token object)
					JsonNode node = a.asJson();
					Iterator<JsonNode> eventsIt = node.has("items")?node.get("items").getElements():null;
					if(eventsIt != null) {
						events = new ArrayList<Event>();
						while (eventsIt.hasNext()) {
							JsonNode eventJson = eventsIt.next();
							Event event = new Event();
							event.setHtmlLink(jNodeToString(eventJson, "htmlLink"));
							event.setSummary(jNodeToString(eventJson, "summary"));
							event.setLocation(jNodeToString(eventJson,"location"));
							event.setStart(DateUtil.parseToDate(jNodeToString(eventJson.get("start"),"dateTime")));
							event.setEnd(DateUtil.parseToDate(jNodeToString(eventJson.get("end"),"dateTime")));
							events.add(event);
						}
					} 
					return events;
				}
			});

		return result;
	}
	
	public static Promise<Response> searchResponse(YouUser user, ClientApiAccess apiAccess, String query) {
		return WS.url(EVENTS_PRIMARY_CALENDAR_URL)
				.setQueryParameter("access_token", user.getGCalendarToken().accessToken)
				.setQueryParameter("client_id", apiAccess.getClientId())
				.setQueryParameter("client_secret", apiAccess.getSecret())
				.setQueryParameter("q", query)
				.setContentType("application/json; charset=UTF-8")
				.get();
		}
	
	
	private static String jNodeToString(JsonNode json, String fieldName) {
		JsonNode field = json!=null?json.get(fieldName):null;
		return field!=null?field.getTextValue():"";
	}
	

}
